package com.onpositive.knowledge.model.values;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;

import com.onpositive.data.IPropertyConstants;
import com.onpositive.knowledge.model.value.audits.AbstractAuditResult;
import com.onpositive.knowledge.model.value.audits.GeneralAuditResult;
import com.onpositive.knowledge.model.value.audits.IdentAudit;
import com.onpositive.knowledge.model.value.audits.IdentAudit.OwnAuditResult;
import com.onpositive.semantic.model.api.property.IProperty;
import com.onpositive.units.ParsedString;
import com.onpositive.units.ParsedValueSequence;

public class IdentPropertyTransformer extends AuditAssistedTransformer {

	public IdentPropertyTransformer(IHasAuditResult source) {
		super(source);
	}

	/**
	 * 
	 */
	private static final long serialVersionUID = -2664074055378387442L;
	
	public static final String ID = "ident-property-transformer" ;

	@Override
	public DescribedValue[] adjust(Object object, IProperty prop, DescribedValue... original)
	{
		if( prop == null )
			return original ;
		
		Boolean isIdent = prop.getMeta().getParentMeta().getSingleValue( IPropertyConstants.CHAR_IS_IDENTIFICATOR, Boolean.class, null) ;
		if( isIdent == null ||  !isIdent )
			return original ;
		String name = prop.getMeta().getSingleValue( "id", String.class, null ) ;
		
		AbstractAuditResult aResult = this.getAuditResultSource().getAuditResult() ;
		
		List<IdentAudit.OwnAuditResult> iar =  null ;
		
		if( aResult instanceof IdentAudit.OwnAuditResult )
			iar = Arrays.asList( new IdentAudit.OwnAuditResult[]{(OwnAuditResult) aResult} ) ;

		else if( aResult instanceof GeneralAuditResult )
		{
			GeneralAuditResult gResult = (GeneralAuditResult) aResult ;
			iar = gResult.getResults( prop.getId(), null, IdentAudit.OwnAuditResult.class ) ;			
		}
		
		if( iar == null || iar.size() == 0 )
			return original;
		
		ArrayList<DescribedValue> result = new ArrayList<DescribedValue>() ;
		
		boolean gotAGlobalChange = false ;
		for( DescribedValue dv : original )
		{
			Object valueObject = dv.value();
			if( valueObject == null || !(valueObject instanceof ParsedValueSequence) )
			{
				result.add(dv) ;
				continue ;
			}
			ParsedValueSequence pvs = (ParsedValueSequence) valueObject ;
			ParsedValueSequence new_pvs = new ParsedValueSequence("IdProp-transf " + pvs.getComment() ) ;
			
			Object parsedStringSet = pvs.value() ;
			if( parsedStringSet == null )
			{
				result.add(dv) ;
				continue ;
			}
			
			LinkedHashSet<ParsedString> set = (LinkedHashSet<ParsedString>) parsedStringSet ;
			
			boolean gotALocalChange = false ;
			String comment = null ;
			//TODO make infered construction more clever
			byte infered = 0;
			for( ParsedString ps : set )
			{
				String str = ps.getValueAsString() ;
				if( str == null || str.length() == 0 )
					continue ;
				
				String newStr = process(str) ;
				if( newStr.equals(str) ){
					new_pvs.addValue(ps) ;					
					continue ;
				}
				else{
					comment = dv.getComment() + ": " + ID  ;
					ParsedString new_ps = new ParsedString( newStr, "IdProp-transf" + ps.getComment() ) ;	
					new_pvs.addValue(new_ps) ;
					gotALocalChange = true ;								
				}
				
			}
			if( gotALocalChange )
			{
				DescribedValue newDv = new DescribedValue( infered, new_pvs, null, comment ) ;
				result.add( newDv) ;
				gotAGlobalChange = true ;
			}
			else
				result.add(dv) ;
			
		}
		return gotAGlobalChange ? result.toArray( new DescribedValue[result.size()] ) : original ;
	}

	private String process(String str) {
		
		boolean gotALink = str.indexOf("[[") >= 0 ;
		String result = processSringWithoutRefs(str) ;
		
		boolean stillGotALink = result.indexOf("[[") >= 0 ;
		boolean lostALink = gotALink != stillGotALink ;
		
		if( lostALink )
			result = str ;
				
		if( result.indexOf("[[") >= 0 )
			result = processSringWithRefs( result ) ;
		
		return result ;
	}

	private static String processSringWithoutRefs(String str)
	{
		if( str.indexOf('(') < 0 )
			return str ;
		
		int l = str.length(), count = 0;
		ArrayList<Integer> posList = new ArrayList<Integer>() ;
		posList.add(0) ;
		int prev = 0 ;
		for( int i = str.indexOf("[[") ; i <= l ;  )
		{
			if( i == l ){
				i++ ;
				continue ;
			}
			if( i < 0 )
				i = l;

			for( int j = prev ; j < i ; j++ )
			{
				if( str.charAt(j) == '(' )
				{
					if( count == 0 )
						posList.add(j) ;
					count++ ;
				}
				if( str.charAt(j) == ')' )
				{
					if( count > 0 ){
						count-- ;
						if( count == 0 )
							posList.add(j+1) ;
					}					
					else{
						posList.add(j) ;
						posList.add(j+1) ;
					}
				}				
			}
			
			if( i < l )
			{
				prev = str.indexOf("]]",i )+2 ;
				i = str.indexOf("[[",prev ) ;
			}			
		}
		if( count > 0 )
			posList.add( l ) ;
		
		posList.add(l) ;
		
		if( posList.size() == 2 )
			return str ;
		
		StringBuilder builder = new StringBuilder() ;
		for( int i = 0 ; i < posList.size() ; i += 2 ){
			builder.append( str.substring( posList.get(i), posList.get(i+1) ).trim() ) ;
			builder.append( " " ) ;
		}
		return builder.toString().trim() ;
	}

	private static String processSringWithRefs(String str)
	{
		int l = str.length(), prev = 0 ;
		ArrayList<Integer> posList = new ArrayList<Integer>() ;
		
		boolean needCut = str.indexOf("[[") > 0 ;
		for( int i = str.indexOf("[[") ; i >= 0 ;  )
		{
			posList.add(i) ;
			int refEnd = str.indexOf("]]", i )+2 ;
			i = str.indexOf("[[", refEnd) ;
			if( i < 0 )
			{
				posList.add( refEnd ) ;
				continue ;
			}
			for( prev = refEnd; prev < l && Character.isWhitespace(str.charAt(prev)) ; prev++ ) ;

			if( prev < l )
			{
				if( str.charAt(prev) == '-' )
					for( prev++ ; prev < l && !Character.isWhitespace(str.charAt(prev)) ; prev++ ) ;
				else
					for( ; prev < l && !Character.isJavaIdentifierPart(str.charAt(prev))
							&& str.charAt(prev) != '[' ; prev++ ) ;
				
				posList.add( prev ) ;
			}
			else
				posList.add( refEnd ) ;			

			i = str.indexOf("[[", prev) ;
			//posList.add( prev ) ;
			if( i >= prev+1 )
				needCut = true ;
		}
		needCut = needCut || posList.get( posList.size()-1) < l ; 
		if( !needCut )
			return str ;
		
		StringBuilder builder = new StringBuilder() ;
		for( int i = 0 ; i < posList.size() ; i += 2 ){
			builder.append( str.substring( posList.get(i), posList.get(i+1) ).trim() ) ;
			builder.append( " " ) ;
		}
		return builder.toString().trim();
	}

	@Override
	public String getId() {
		return ID;
	}


}
